package com.yupi.springbootinit.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.mapper.*;
import com.yupi.springbootinit.model.dto.production.printingrecord.CompleteProcessRequest;
import com.yupi.springbootinit.model.dto.production.printingrecord.StartProcessRequest;
import com.yupi.springbootinit.model.dto.production.printingrecord.StartProductionFlowRequest;
import com.yupi.springbootinit.model.entity.*;
import com.yupi.springbootinit.model.enums.StepEvent;
import com.yupi.springbootinit.model.enums.StepStatus;
import com.yupi.springbootinit.model.vo.production.CleaningRecordVO;
import com.yupi.springbootinit.model.vo.production.ProcessCompleteVO;
import com.yupi.springbootinit.model.vo.production.ProcessStepVO;
import com.yupi.springbootinit.model.vo.production.ProductionTaskDetailVO;
import com.yupi.springbootinit.service.ProductionCleaningService;
import com.yupi.springbootinit.service.ProductionProcessService;
import com.yupi.springbootinit.service.SalesOrderService;
import com.yupi.springbootinit.service.workflow.WorkflowTaskCreationService;
import com.yupi.springbootinit.statemachine.step.StepStateMachine;
import com.yupi.springbootinit.statemachine.step.StepTransitionContext;
import com.yupi.springbootinit.statemachine.step.impl.DefaultStepStateMachine;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 生产工序服务实现
 */
@Slf4j
@Service
public class ProductionProcessServiceImpl implements ProductionProcessService {

    // ==================== 常量定义 ====================
    private static final String STATUS_PENDING = "PENDING";
    private static final String STATUS_IN_PROGRESS = "IN_PROGRESS";
    private static final String STATUS_DONE = "DONE";

    private static final String STEP_STATUS_PLANNED = "PLANNED";
    private static final String STEP_STATUS_READY = "READY";
    private static final String STEP_STATUS_IN_PROGRESS = "IN_PROGRESS";
    private static final String STEP_STATUS_PENDING_QA = "PENDING_QA";
    private static final String STEP_STATUS_COMPLETED = "COMPLETED";

    private static final String EVT_START = "START";
    private static final String EVT_COMPLETE = "COMPLETE";

    private static final long OVERDUE_TOLERANCE_MINUTES = 5L;

    // ==================== 依赖注入 ====================
    @Resource
    private WorkflowTaskMapper workflowTaskMapper;
    @Resource
    private ProcessBatchStepMapper processBatchStepMapper;

    @Resource
    private SalesOrderItemMapper salesOrderItemMapper;
    @Resource
    private ProcessBatchMapper processBatchMapper;
    @Resource
    private ProcessSheetStepMapper processSheetStepMapper;

    @Resource
    private ProcessSheetBatchMapper processSheetBatchMapper;

    @Resource
    private ProcessSheetBatchProductMapper processSheetBatchProductMapper;

    @Resource
    private ProcessSheetProductMapper processSheetProductMapper;

    @Resource
    private ProductionOperationRecordMapper productionOperationRecordMapper;
    @Resource
    private UserMapper userMapper;


    @Resource
    private ProductionCleaningService productionCleaningService;
    @Resource
    private WorkflowTaskCreationService taskCreationService;
    @Resource
    private SalesOrderService salesOrderService;

    private final StepStateMachine stepStateMachine = new DefaultStepStateMachine();

    // ==================== 接口实现 ====================

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean startProduction(StartProductionFlowRequest request, Long loginUserId, String loginUserName) {
        if (request == null || request.getTaskId() == null || request.getFirstStepId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不合法");
        }
        if (StringUtils.isBlank(request.getOperatorName())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "操作员姓名不能为空");
        }

        Long taskId = request.getTaskId();
        Long stepId = request.getFirstStepId();

        WorkflowTask task = workflowTaskMapper.selectById(taskId);
        if (task == null || task.getIsDelete() == 1) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "任务不存在");
        }

        CleaningRecordVO cleaningRecord = productionCleaningService.getCleaningRecord(taskId);
        if (cleaningRecord == null || !"COMPLETED".equals(cleaningRecord.getStatus())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "请先完成烘粉清机");
        }

        ProcessBatchStep firstStep = processBatchStepMapper.selectById(stepId);
        if (firstStep == null || firstStep.getIsDeleted() == 1) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "工序不存在");
        }
        if (firstStep.getSequenceNo() == null || firstStep.getSequenceNo() != 1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "必须从第一道工序开始");
        }

        LocalDateTime now = LocalDateTime.now();
        if (firstStep.getPlanStart() != null && now.isBefore(firstStep.getPlanStart())) {
            if (request.getForceStart() == null || !request.getForceStart()) {
                long minutesEarly = java.time.Duration.between(now, firstStep.getPlanStart()).toMinutes();
                throw new BusinessException(ErrorCode.OPERATION_ERROR, String.format("当前时间未到计划开始时间（提前%d分钟），如需提前开始请设置forceStart=true并填写原因", minutesEarly));
            }
            if (StringUtils.isBlank(request.getEarlyStartReason())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "提前开始必须填写原因");
            }
        }

        StepTransitionContext ctx = new StepTransitionContext();
        ctx.setNow(now);
        ctx.setPlanStart(firstStep.getPlanStart());

        StepStatus currentStatus = StepStatus.from(firstStep.getStatus());
        if (currentStatus == null) currentStatus = StepStatus.READY;

        StepStatus nextStatus = stepStateMachine.transit(currentStatus, StepEvent.START, ctx);

        firstStep.setStatus(nextStatus.code());
        firstStep.setActualStart(now);
        firstStep.setUpdateTime(now);
        processBatchStepMapper.updateById(firstStep);

        OperatorInfo operator = getOperatorInfo(request.getOperatorId(), request.getOperatorName(), request.getOperatorAccount());

        ProductionOperationRecord record = new ProductionOperationRecord();
        record.setTaskId(taskId);
        record.setBatchId(firstStep.getBatchId());
        record.setStepId(stepId);
        record.setOperationSeq(firstStep.getSequenceNo());
        record.setExecType("PROCESS_EXECUTION");
        record.setEventType(EVT_START);
        record.setEventTime(toDate(now));
        record.setOperatorId(operator.getOperatorId());
        record.setOperatorName(operator.getOperatorName());
        record.setOperatorCode(operator.getOperatorAccount());
        record.setCreatedBy(loginUserId);
        record.setCreatedByName(loginUserName);

        StringBuilder remarkBuilder = new StringBuilder();
        if (Boolean.TRUE.equals(request.getForceStart())) {
            remarkBuilder.append("提前开始原因: ").append(request.getEarlyStartReason());
        }
        if (StringUtils.isNotBlank(request.getRemark())) {
            if (remarkBuilder.length() > 0) remarkBuilder.append("; ");
            remarkBuilder.append(request.getRemark());
        }
        record.setRemark(remarkBuilder.toString());

        int rows = productionOperationRecordMapper.insert(record);
        if (rows > 0) {
            // 更新任务状态为IN_PROGRESS
            UpdateWrapper<WorkflowTask> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id", taskId).set("taskStatus", STATUS_IN_PROGRESS).set("updateTime", now);
            workflowTaskMapper.update(null, updateWrapper);

            log.info("生产流程已开始: taskId={}, stepId={}, operator={}({}), forceStart={}", taskId, stepId, operator.getOperatorName(), operator.getOperatorAccount(), request.getForceStart());

            // ✅ 订单状态推进：开始第一道工序 → IN_PRODUCTION
            try {
                String orderNumber = task.getBizNumber();
                if (StringUtils.isBlank(orderNumber)) {
                    log.debug("任务缺少订单号，跳过订单状态更新: taskId={}", taskId);
                    return rows > 0;
                }

                SalesOrder order = salesOrderService.getByOrderNumber(orderNumber);
                if (order == null) {
                    log.warn("⚠️ 未找到订单，无法更新状态: orderNumber={}", orderNumber);
                    return rows > 0;
                }

                String orderCurrentStatus = order.getOrderStatus();

                // ✅ 幂等性检查
                if ("IN_PRODUCTION".equals(orderCurrentStatus) || "INSPECTING".equals(orderCurrentStatus) || "COMPLETED".equals(orderCurrentStatus)) {
                    log.info("订单状态已是 {} ，无需更新为 IN_PRODUCTION: orderId={}, orderNumber={}", orderCurrentStatus, order.getId(), orderNumber);
                    return rows > 0;
                }

                // 更新订单状态
                salesOrderService.updateOrderStatus(order.getId(), "IN_PRODUCTION", "正在生产中", null);
                log.info("✅ 订单状态更新为 IN_PRODUCTION: orderId={}, orderNumber={}, 原状态={}", order.getId(), orderNumber, orderCurrentStatus);

            } catch (Exception e) {
                log.error("❌ 更新订单状态为 IN_PRODUCTION 失败: taskId={}, bizNumber={}, err={}", taskId, task.getBizNumber(), e.getMessage(), e);
            }
        }
        return rows > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean startProcess(StartProcessRequest request, Long loginUserId, String loginUserName) {
        if (request == null || request.getStepId() == null) {
            log.warn("工序开始参数不合法: request={}", request);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不合法");
        }
        if (StringUtils.isBlank(request.getOperatorName())) {
            log.warn("工序开始参数不合法: operatorName is blank");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "操作员姓名不能为空");
        }

        Long stepId = request.getStepId();
        ProcessBatchStep step = processBatchStepMapper.selectById(stepId);
        if (step == null || step.getIsDeleted() == 1) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "工序不存在");
        }

        StepStatus currentStatus = StepStatus.from(step.getStatus());
        if (currentStatus != StepStatus.READY && currentStatus != StepStatus.PLANNED) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "工序状态不允许开始，当前状态：" + step.getStatus());
        }

        if (step.getSequenceNo() != null && step.getSequenceNo() > 1) {
            LambdaQueryWrapper<ProcessBatchStep> prevQw = new LambdaQueryWrapper<>();
            prevQw.eq(ProcessBatchStep::getBatchId, step.getBatchId())
                    .eq(ProcessBatchStep::getSequenceNo, step.getSequenceNo() - 1)
                    .eq(ProcessBatchStep::getIsDeleted, 0)
                    .last("LIMIT 1");
            ProcessBatchStep prevStep = processBatchStepMapper.selectOne(prevQw);
            if (prevStep != null && !STEP_STATUS_COMPLETED.equals(prevStep.getStatus())) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "请等待质检完成");
            }
        }

        LocalDateTime now = LocalDateTime.now();

        String overdueReason = request.getRemark();
        if (step.getPlanStart() != null && now.isAfter(step.getPlanStart().plusMinutes(OVERDUE_TOLERANCE_MINUTES))) {
            long overdueMinutes = java.time.Duration.between(step.getPlanStart(), now).toMinutes();
            if (StringUtils.isBlank(overdueReason)) {
                overdueReason = String.format("工序逾期启动（逾期%d分钟）", overdueMinutes);
            }
            log.warn("工序逾期启动: stepId={}, 计划开始={}, 实际开始={}, 逾期{}分钟", stepId, step.getPlanStart(), now, overdueMinutes);
        }

        OperatorInfo operator = getOperatorInfo(request.getOperatorId(), request.getOperatorName(), request.getOperatorAccount());

        StepTransitionContext ctx = StepTransitionContext.builder()
                .now(now)
                .planStart(step.getPlanStart())
                .previousCompleted(checkPreviousCompleted(step))
                .overdueReason(overdueReason)
                .build();

        log.info("创建状态转换上下文: stepId={}, overdueReason={}", stepId, overdueReason);

        if (currentStatus == StepStatus.PLANNED) {
            log.info("工序从 PLANNED 自动转换为 READY: stepId={}", stepId);
            currentStatus = stepStateMachine.transit(StepStatus.PLANNED, StepEvent.MAKE_READY, ctx);

            UpdateWrapper<ProcessBatchStep> readyUpdate = new UpdateWrapper<>();
            readyUpdate.eq("id", stepId)
                    .eq("version", step.getVersion())
                    .eq("is_deleted", 0)
                    .set("status", currentStatus.code())
                    .set("update_time", now)
                    .set("version", step.getVersion() + 1);

            int readyRows = processBatchStepMapper.update(null, readyUpdate);
            if (readyRows == 0) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "工序状态已被修改，请刷新后重试");
            }

            step.setVersion(step.getVersion() + 1);
        }

        log.info("开始状态转换: from={}, event=START, overdueReason={}", currentStatus, ctx.getOverdueReason());
        StepStatus nextStatus = stepStateMachine.transit(currentStatus, StepEvent.START, ctx);

        UpdateWrapper<ProcessBatchStep> startUpdate = new UpdateWrapper<>();
        startUpdate.eq("id", stepId)
                .eq("version", step.getVersion())
                .eq("is_deleted", 0)
                .set("status", nextStatus.code())
                .set("actual_start", now)
                .set("operator_name", operator.getOperatorName())
                .set("update_time", now)
                .set("version", step.getVersion() + 1);

        int startRows = processBatchStepMapper.update(null, startUpdate);
        if (startRows == 0) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "工序状态已被修改，请刷新后重试");
        }

        ProductionOperationRecord record = new ProductionOperationRecord();
        record.setTaskId(extractTaskIdFromStep(step));
        record.setBatchId(step.getBatchId());
        record.setStepId(stepId);
        record.setOperationSeq(step.getSequenceNo());
        record.setExecType("PROCESS_EXECUTION");
        record.setEventType(EVT_START);
        record.setEventTime(toDate(now));
        record.setOperatorId(operator.getOperatorId());
        record.setOperatorName(operator.getOperatorName());
        record.setOperatorCode(operator.getOperatorAccount());
        record.setCreatedBy(loginUserId);
        record.setCreatedByName(loginUserName);
        record.setRemark(overdueReason);

        int rows = productionOperationRecordMapper.insert(record);

        if (rows > 0) {
            log.info("工序已开始: stepId={}, PLANNED->READY->IN_PROGRESS, operator={}({}), recordedBy={}",
                    stepId, operator.getOperatorName(), operator.getOperatorAccount(), loginUserName);
        }
        return rows > 0;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProcessCompleteVO completeProcess(CompleteProcessRequest request, Long loginUserId, String loginUserName) {
        // ==================== 参数校验 ====================
        if (request == null || request.getStepId() == null) {
            log.warn("工序完成参数不合法: request={}", request);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不合法");
        }
        if (request.getIsNormal() == null) {
            log.warn("工序完成参数不合法: isNormal is null");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请选择是否正常完成");
        }

        // ==================== 查询工序信息 ====================
        Long stepId = request.getStepId();
        ProcessBatchStep step = processBatchStepMapper.selectById(stepId);
        if (step == null || step.getIsDeleted() == 1) {
            log.warn("工序不存在或已被删除: stepId={}", stepId);
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "工序不存在");
        }
        if (!STEP_STATUS_IN_PROGRESS.equals(step.getStatus())) {
            log.warn("工序状态不正确: stepId={}, expected=IN_PROGRESS, actual={}", stepId, step.getStatus());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "工序状态不正确，当前状态：" + step.getStatus());
        }

        LocalDateTime now = LocalDateTime.now();

        // ==================== 判断是否需要质检 ====================
        boolean needInspection = (step.getNeedInspection() != null && step.getNeedInspection() == 1);

        log.info("工序检验判断: stepId={}, stepName={}, needInspection={}", step.getId(), step.getOperationName(), needInspection);

        // ==================== P0-1 修复：质检任务预创建（带重试机制） ====================
        if (needInspection) {
            Long taskId = extractTaskIdFromStep(step);
            WorkflowTask task = taskId != null ? workflowTaskMapper.selectById(taskId) : null;
            String orderNumber = task != null ? task.getBizNumber() : null;

            log.info("🚀 [质检任务预创建] stepId={}, stepName={}, batchId={}, orderNumber={}", step.getId(), step.getOperationName(), step.getBatchId(), orderNumber);

            // 重试3次创建质检任务
            boolean taskCreated = false;
            Exception lastException = null;

            for (int attempt = 1; attempt <= 3; attempt++) {
                try {
                    taskCreationService.createQualityInspectionTask(step.getId(), step.getBatchId(), orderNumber, step.getOperationName(), step.getSequenceNo(), loginUserId, loginUserName);

                    taskCreated = true;
                    log.info("✅ [质检任务预创建成功] stepId={}, attempt={}, taskType=QUALITY_INSPECTION", step.getId(), attempt);
                    break;

                } catch (Exception e) {
                    lastException = e;
                    log.warn("⚠️ [质检任务创建失败] stepId={}, attempt={}/3, err={}", step.getId(), attempt, e.getMessage());

                    if (attempt < 3) {
                        // 等待100ms后重试
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                            log.warn("重试等待被中断");
                            break;
                        }
                    }
                }
            }

            // 3次都失败，记录错误日志但不抛异常，允许主流程继续
            if (!taskCreated) {
                log.error("❌ [质检任务创建失败，已重试3次] stepId={}, stepName={}, 工序状态已更新为PENDING_QA，" + "但品质部待办创建失败。请手动检查并补偿。最后异常: {}", step.getId(), step.getOperationName(), lastException != null ? lastException.getMessage() : "未知错误", lastException);

                // 记录到审计日志表，便于后续排查和补偿
                try {
                    ProductionOperationRecord errorRecord = new ProductionOperationRecord();
                    errorRecord.setTaskId(taskId);
                    errorRecord.setBatchId(step.getBatchId());
                    errorRecord.setStepId(stepId);
                    errorRecord.setOperationSeq(step.getSequenceNo());
                    errorRecord.setExecType("QA_TASK_CREATION");
                    errorRecord.setEventType("ERROR");
                    errorRecord.setEventTime(toDate(now));
                    errorRecord.setCreatedBy(loginUserId);
                    errorRecord.setCreatedByName(loginUserName);
                    errorRecord.setRemark(String.format("质检任务创建失败（已重试3次）: %s", lastException != null ? lastException.getMessage() : "未知错误"));
                    productionOperationRecordMapper.insert(errorRecord);
                } catch (Exception e) {
                    log.error("记录质检任务创建失败日志时出错", e);
                }
            }
        }

        // ==================== 状态转换 ====================
        log.info("========== 工序完成状态转换 ==========");
        log.info("stepId={}, needInspection={}, currentStatus={}", step.getId(), step.getNeedInspection(), step.getStatus());

        StepTransitionContext ctx = new StepTransitionContext();
        ctx.setNeedInspection(step.getNeedInspection());
        StepStatus nextStatus = stepStateMachine.transit(StepStatus.IN_PROGRESS, StepEvent.COMPLETE, ctx);

        log.info("状态转换结果: {} → {}, needInspection={}", StepStatus.IN_PROGRESS.code(), nextStatus.code(), step.getNeedInspection());
        log.info("=======================================");

        // ==================== 更新工序状态 ====================
        UpdateWrapper<ProcessBatchStep> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", stepId).eq("version", step.getVersion()).eq("is_deleted", 0).set("status", nextStatus.code()).set("actual_end", now).set("update_time", now).set("version", step.getVersion() + 1);

        int updateRows = processBatchStepMapper.update(null, updateWrapper);
        if (updateRows == 0) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "工序状态已被修改，请刷新后重试");
        }

        log.info("✅ 工序状态更新成功: stepId={}, updateRows={}, status={}", stepId, updateRows, nextStatus.code());

        // ==================== 获取任务信息 ====================
        Long taskId = extractTaskIdFromStep(step);
        WorkflowTask task = taskId != null ? workflowTaskMapper.selectById(taskId) : null;

        // ==================== 计算实际耗时和逾期情况 ====================
        Long actualDuration = null;
        Boolean isOvertime = false;
        Long overtimeMinutes = 0L;
        if (step.getActualStart() != null) {
            actualDuration = java.time.Duration.between(step.getActualStart(), now).toMinutes();
            if (step.getPlanEnd() != null && now.isAfter(step.getPlanEnd())) {
                isOvertime = true;
                overtimeMinutes = java.time.Duration.between(step.getPlanEnd(), now).toMinutes();
            }
        }

        // ==================== 记录操作日志 ====================
        ProductionOperationRecord record = new ProductionOperationRecord();
        record.setTaskId(taskId);
        record.setBatchId(step.getBatchId());
        record.setStepId(stepId);
        record.setOperationSeq(step.getSequenceNo());
        record.setExecType("PROCESS_EXECUTION");
        record.setEventType(EVT_COMPLETE);
        record.setEventTime(toDate(now));
        record.setCreatedBy(loginUserId);
        record.setCreatedByName(loginUserName);

        String remarkText = request.getIsNormal() ? "正常完成" : "异常完成";
        if (!request.getIsNormal() && StringUtils.isBlank(request.getRemark())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "异常完成必须填写原因");
        }
        if (StringUtils.isNotBlank(request.getRemark())) {
            remarkText += ": " + request.getRemark();
        }
        if (isOvertime) {
            remarkText += String.format(" [逾期%d分钟]", overtimeMinutes);
            if (!request.getIsNormal()) {
                log.error("工序异常完成且逾期: stepId={}, 逾期{}分钟", stepId, overtimeMinutes);
            } else {
                log.warn("工序正常完成但已逾期: stepId={}, 逾期{}分钟", stepId, overtimeMinutes);
            }
        }
        record.setRemark(remarkText);
        productionOperationRecordMapper.insert(record);

        // ==================== 查询下一工序 ====================
        LambdaQueryWrapper<ProcessBatchStep> nextQw = new LambdaQueryWrapper<>();
        nextQw.eq(ProcessBatchStep::getBatchId, step.getBatchId()).eq(ProcessBatchStep::getSequenceNo, step.getSequenceNo() + 1).eq(ProcessBatchStep::getIsDeleted, 0).last("LIMIT 1");
        ProcessBatchStep nextStep = processBatchStepMapper.selectOne(nextQw);

        boolean isLastProcess = (nextStep == null);

        // ==================== 组装返回结果 ====================
        ProcessCompleteVO vo = new ProcessCompleteVO();
        vo.setProcessId(stepId);
        vo.setProcessName(step.getOperationName());
        vo.setActualEndTime(now);
        vo.setActualDurationMinutes(actualDuration);
        vo.setIsOvertime(isOvertime);
        vo.setOvertimeMinutes(overtimeMinutes);
        vo.setHasNext(nextStep != null);
        vo.setIsLastProcess(isLastProcess);
        if (nextStep != null) {
            vo.setNextProcess(convertToProcessStepVO(nextStep));
        }
        if (isLastProcess) {
            log.info("最后工序已完成，开始完成批次任务: stepId={}, batchId={}", stepId, step.getBatchId());

            UpdateWrapper<ProcessBatch> batchUpdate = new UpdateWrapper<>();
            batchUpdate.eq("id", step.getBatchId()).eq("is_deleted", 0).set("status", "COMPLETED").set("update_time", now);
            int batchRows = processBatchMapper.update(null, batchUpdate);

            if (batchRows > 0) {
                log.info("批次状态已更新为COMPLETED: batchId={}", step.getBatchId());

                if (taskId != null) {
                    UpdateWrapper<WorkflowTask> taskUpdate = new UpdateWrapper<>();
                    taskUpdate.eq("id", taskId).eq("isDelete", 0).set("taskStatus", STATUS_DONE).set("completedTime", now).set("updateTime", now);
                    int taskRows = workflowTaskMapper.update(null, taskUpdate);

                    if (taskRows > 0) {
                        log.info("生产任务状态已更新为DONE: taskId={}", taskId);

                        checkAndCompleteOrder(task, loginUserId, loginUserName);
                    } else {
                        log.warn("更新生产任务状态失败: taskId={}", taskId);
                    }
                } else {
                    log.warn("未找到生产任务: stepId={}, batchId={}", stepId, step.getBatchId());
                }
            } else {
                log.warn("更新批次状态失败: batchId={}", step.getBatchId());
            }
        }

        return vo;
    }

    @Override
    public ProductionTaskDetailVO getProductionTaskDetail(Long taskId) {
        if (taskId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "任务ID不能为空");
        }

        log.info("========== 查询生产任务详情 ==========");
        log.info("taskId={}", taskId);

        ProductionTaskDetailVO vo = new ProductionTaskDetailVO();

        // ========== 1. 查询任务基本信息 ==========
        WorkflowTask task = workflowTaskMapper.selectById(taskId);
        if (task == null || task.getIsDelete() == 1) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "任务不存在");
        }

        vo.setTaskId(taskId);
        vo.setTaskType(task.getTaskType());
        vo.setTaskStatus(task.getTaskStatus());

        // ========== 2. 提取批次ID ==========
        Long batchId = extractBatchIdFromTask(task);
        if (batchId == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "任务未关联批次");
        }
        vo.setBatchId(batchId);

        // ========== 3. 查询批次基本信息 ==========
        ProcessBatch batch = processBatchMapper.selectById(batchId);
        if (batch == null || (batch.getIsDeleted() != null && batch.getIsDeleted() == 1)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "批次不存在");
        }

        vo.setBatchCode(batch.getBatchCode());
        vo.setOrderNumber(batch.getOrderNumber());
        vo.setFlowNumber(batch.getFlowNumber());
        vo.setProductionPlanNumber(batch.getProductionPlanNumber());

        // ========== 4. 查询批次序号 ==========
        if (batch.getBatchPlanId() != null) {
            try {
                ProcessSheetBatch batchPlan = processSheetBatchMapper.selectById(batch.getBatchPlanId());
                if (batchPlan != null) {
                    vo.setBatchNo(batchPlan.getBatchNo());
                    log.info("✅ 批次序号: batchNo={}", batchPlan.getBatchNo());
                } else {
                    log.warn("⚠️ 未找到批次计划: batchPlanId={}", batch.getBatchPlanId());
                }
            } catch (Exception e) {
                log.warn("查询批次序号失败: batchPlanId={}", batch.getBatchPlanId(), e);
            }
        } else {
            log.warn("⚠️ 批次未关联批次计划: batchId={}", batchId);
        }

        // ========== 5. 查询批次产品列表 ==========
        if (batch.getBatchPlanId() != null) {
            try {
                List<ProcessSheetBatchProduct> batchProducts = processSheetBatchProductMapper.selectList(new LambdaQueryWrapper<ProcessSheetBatchProduct>().eq(ProcessSheetBatchProduct::getBatchId, batch.getBatchPlanId()).eq(ProcessSheetBatchProduct::getIsDeleted, 0));

                if (batchProducts != null && !batchProducts.isEmpty()) {
                    // 批量查询产品详细信息
                    Set<Long> productIds = batchProducts.stream().map(ProcessSheetBatchProduct::getProductId).filter(Objects::nonNull).collect(Collectors.toSet());

                    Map<Long, ProcessSheetProduct> productMap = new HashMap<>();
                    if (!productIds.isEmpty()) {
                        List<ProcessSheetProduct> sheetProducts = processSheetProductMapper.selectBatchIds(new ArrayList<>(productIds));
                        if (sheetProducts != null && !sheetProducts.isEmpty()) {
                            productMap = sheetProducts.stream().collect(Collectors.toMap(ProcessSheetProduct::getId, p -> p));
                        }
                    }

                    Map<Long, ProcessSheetProduct> finalProductMap = productMap;
                    List<ProductionTaskDetailVO.BatchProductVO> productVOs = batchProducts.stream().map(p -> {
                        ProductionTaskDetailVO.BatchProductVO pvo = new ProductionTaskDetailVO.BatchProductVO();
                        pvo.setProductCode(p.getProductCode());
                        pvo.setProductName(p.getProductName());
                        pvo.setProductionQuantity(p.getProductionQuantity());

                        if (p.getProductId() != null) {
                            ProcessSheetProduct sheetProduct = finalProductMap.get(p.getProductId());
                            if (sheetProduct != null) {
                                pvo.setSpecification(sheetProduct.getSpecification());
                                pvo.setProductHeight(sheetProduct.getProductHeight());
                                pvo.setPartDrawingNo(sheetProduct.getPartDrawingNo());
                                pvo.setUnitWeight(sheetProduct.getUnitWeight());
                            }
                        }

                        return pvo;
                    }).collect(Collectors.toList());

                    vo.setProducts(productVOs);
                    log.info("✅ 批次产品列表: 产品数量={}", productVOs.size());
                } else {
                    log.warn("⚠️ 批次无产品数据: batchPlanId={}", batch.getBatchPlanId());
                }
            } catch (Exception e) {
                log.error("查询批次产品列表失败: batchPlanId={}", batch.getBatchPlanId(), e);
            }
        }

        // ========== 6. 查询工序列表 ==========
        LambdaQueryWrapper<ProcessBatchStep> qw = new LambdaQueryWrapper<>();
        qw.eq(ProcessBatchStep::getBatchId, batchId).eq(ProcessBatchStep::getIsDeleted, 0).orderByAsc(ProcessBatchStep::getSequenceNo);
        List<ProcessBatchStep> steps = processBatchStepMapper.selectList(qw);

        if (steps != null && !steps.isEmpty()) {
            // 批量查询工艺单工序详情
            Set<Long> sheetStepIds = steps.stream().map(ProcessBatchStep::getProcessSheetStepId).filter(Objects::nonNull).collect(Collectors.toSet());

            Map<Long, ProcessSheetStep> sheetStepMap = new HashMap<>();
            if (!sheetStepIds.isEmpty()) {
                List<ProcessSheetStep> sheetSteps = processSheetStepMapper.selectBatchIds(new ArrayList<>(sheetStepIds));
                if (sheetSteps != null && !sheetSteps.isEmpty()) {
                    sheetStepMap = sheetSteps.stream().collect(Collectors.toMap(ProcessSheetStep::getId, s -> s));
                }
            }

            // 批量查询操作员信息
            Map<Long, String> operatorNameMap = batchQueryOperatorNames(steps);

            // 转换为 VO
            Map<Long, ProcessSheetStep> finalSheetStepMap = sheetStepMap;
            Map<Long, String> finalOperatorMap = operatorNameMap;
            List<ProcessStepVO> stepVOs = steps.stream().map(step -> convertToProcessStepVO(step, finalSheetStepMap, finalOperatorMap)).collect(Collectors.toList());

            vo.setSteps(stepVOs);
            log.info("✅ 工序列表: 工序数量={}", stepVOs.size());
        } else {
            log.warn("⚠️ 批次无工序数据: batchId={}", batchId);
        }

        log.info("========== 生产任务详情查询完成 ==========");
        log.info("batchId={}, batchNo={}, 产品数={}, 工序数={}", batchId, vo.getBatchNo(), vo.getProducts() != null ? vo.getProducts().size() : 0, vo.getSteps() != null ? vo.getSteps().size() : 0);

        return vo;
    }


    // ==================== 私有辅助方法 ====================

    /**
     * 批量查询工序的操作员姓名
     */
    private Map<Long, String> batchQueryOperatorNames(List<ProcessBatchStep> steps) {
        if (steps == null || steps.isEmpty()) {
            return Collections.emptyMap();
        }

        List<Long> startedStepIds = steps.stream().filter(s -> s.getActualStart() != null).map(ProcessBatchStep::getId).collect(Collectors.toList());

        if (startedStepIds.isEmpty()) {
            return Collections.emptyMap();
        }

        LambdaQueryWrapper<ProductionOperationRecord> qw = new LambdaQueryWrapper<>();
        qw.in(ProductionOperationRecord::getStepId, startedStepIds).eq(ProductionOperationRecord::getEventType, EVT_START).eq(ProductionOperationRecord::getIsDelete, 0).orderByDesc(ProductionOperationRecord::getEventTime);

        List<ProductionOperationRecord> records = productionOperationRecordMapper.selectList(qw);

        Map<Long, String> operatorMap = new HashMap<>();
        for (ProductionOperationRecord record : records) {
            if (!operatorMap.containsKey(record.getStepId())) {
                operatorMap.put(record.getStepId(), record.getOperatorName());
            }
        }

        log.debug("批量查询操作员信息完成: 工序数={}, 查询到操作员数={}", startedStepIds.size(), operatorMap.size());

        return operatorMap;
    }

    /**
     * 转换批次工序为 VO（优化版：批量查询，避免 N+1 问题）
     */
    private ProcessStepVO convertToProcessStepVO(ProcessBatchStep step, Map<Long, ProcessSheetStep> sheetStepMap, Map<Long, String> operatorMap) {
        ProcessStepVO vo = new ProcessStepVO();

        vo.setId(step.getId());
        vo.setBatchId(step.getBatchId());
        vo.setSequenceNo(step.getSequenceNo());
        vo.setOperationName(step.getOperationName());
        vo.setOperationCode(step.getOperationCode());
        vo.setPlanStart(step.getPlanStart());
        vo.setPlanEnd(step.getPlanEnd());
        vo.setExpectedDurationMinutes(step.getExpectedDurationMinutes());
        vo.setActualStart(step.getActualStart());
        vo.setActualEnd(step.getActualEnd());
        vo.setStatus(step.getStatus());
        vo.setStatusName(getStepStatusDisplayName(step.getStatus()));
        vo.setNeedInspection(step.getNeedInspection());
        vo.setRemark(step.getRemark());

        if (step.getProcessSheetStepId() != null) {
            ProcessSheetStep sheetStep = sheetStepMap.get(step.getProcessSheetStepId());
            if (sheetStep != null) {
                vo.setInspectionMethod(sheetStep.getInspectionMethod());
                vo.setProcessRequirement(sheetStep.getProcessRequirement());
                vo.setInspectionCriteria(sheetStep.getInspectionCriteria());
                vo.setIsOutsourced(sheetStep.getIsOutsourced());
                vo.setIsInspectionRequired(sheetStep.getIsInspectionRequired());
            }
        }

        if (step.getActualStart() != null) {
            String operatorName = operatorMap.get(step.getId());
            if (operatorName != null) {
                vo.setOperatorName(operatorName);
            }
        }

        calculateOverdueStatus(step, vo);

        return vo;
    }

    /**
     * 转换批次工序为 VO（兼容版本）
     */
    private ProcessStepVO convertToProcessStepVO(ProcessBatchStep step) {
        Map<Long, ProcessSheetStep> sheetStepMap = new HashMap<>();
        if (step.getProcessSheetStepId() != null) {
            try {
                ProcessSheetStep sheetStep = processSheetStepMapper.selectById(step.getProcessSheetStepId());
                if (sheetStep != null) {
                    sheetStepMap.put(sheetStep.getId(), sheetStep);
                }
            } catch (Exception e) {
                log.warn("查询工艺单工序详情失败: processSheetStepId={}", step.getProcessSheetStepId(), e);
            }
        }

        Map<Long, String> operatorMap = batchQueryOperatorNames(Collections.singletonList(step));
        return convertToProcessStepVO(step, sheetStepMap, operatorMap);
    }

    /**
     * 计算工序的逾期状态
     */
    private void calculateOverdueStatus(ProcessBatchStep step, ProcessStepVO vo) {
        LocalDateTime now = LocalDateTime.now();

        // 启动逾期判断
        if (step.getActualStart() == null && step.getPlanStart() != null) {
            LocalDateTime threshold = step.getPlanStart().plusMinutes(OVERDUE_TOLERANCE_MINUTES);

            if (now.isAfter(threshold)) {
                vo.setStartOverdue(true);
                long overdueMinutes = java.time.Duration.between(step.getPlanStart(), now).toMinutes();
                vo.setStartOverdueMinutes(overdueMinutes);

                log.debug("工序启动逾期: stepId={}, planStart={}, now={}, overdue={}分钟", step.getId(), step.getPlanStart(), now, overdueMinutes);
            } else {
                vo.setStartOverdue(false);
                vo.setStartOverdueMinutes(0L);
            }
        } else {
            vo.setStartOverdue(false);
            vo.setStartOverdueMinutes(0L);
        }

        // 完成逾期判断
        if (step.getActualStart() != null && step.getActualEnd() == null && step.getPlanEnd() != null) {
            if (now.isAfter(step.getPlanEnd())) {
                vo.setCompletionOverdue(true);
                long overdueMinutes = java.time.Duration.between(step.getPlanEnd(), now).toMinutes();
                vo.setCompletionOverdueMinutes(overdueMinutes);

                log.debug("工序完成逾期: stepId={}, planEnd={}, now={}, overdue={}分钟", step.getId(), step.getPlanEnd(), now, overdueMinutes);
            } else {
                vo.setCompletionOverdue(false);
                vo.setCompletionOverdueMinutes(0L);
            }
        } else {
            vo.setCompletionOverdue(false);
            vo.setCompletionOverdueMinutes(0L);
        }
    }

    /**
     * 获取工序状态显示名称
     */
    private String getStepStatusDisplayName(String status) {
        if (status == null) return "";
        StepStatus stepStatus = StepStatus.from(status);
        return stepStatus != null ? stepStatus.displayName() : status;
    }

    /**
     * 检查前序步骤是否完成
     */
    private Boolean checkPreviousCompleted(ProcessBatchStep currentStep) {
        if (currentStep.getSequenceNo() == null || currentStep.getSequenceNo() == 1) {
            return true;
        }

        LambdaQueryWrapper<ProcessBatchStep> qw = new LambdaQueryWrapper<>();
        qw.eq(ProcessBatchStep::getBatchId, currentStep.getBatchId()).eq(ProcessBatchStep::getSequenceNo, currentStep.getSequenceNo() - 1).eq(ProcessBatchStep::getIsDeleted, 0).last("LIMIT 1");

        ProcessBatchStep prevStep = processBatchStepMapper.selectOne(qw);
        return prevStep != null && STEP_STATUS_COMPLETED.equals(prevStep.getStatus());
    }

    /**
     * 从工序提取任务ID
     */
    private Long extractTaskIdFromStep(ProcessBatchStep step) {
        if (step == null || step.getBatchId() == null) return null;
        LambdaQueryWrapper<WorkflowTask> qw = new LambdaQueryWrapper<>();
        qw.eq(WorkflowTask::getBizType, "PROCESS_BATCH").eq(WorkflowTask::getBizId, step.getBatchId()).eq(WorkflowTask::getTaskType, "PRODUCTION_WORKORDER").eq(WorkflowTask::getIsDelete, 0).last("LIMIT 1");
        WorkflowTask task = workflowTaskMapper.selectOne(qw);
        return task != null ? task.getId() : null;
    }

    /**
     * 从任务提取批次ID
     */
    private Long extractBatchIdFromTask(Long taskId) {
        if (taskId == null) return null;
        WorkflowTask task = workflowTaskMapper.selectById(taskId);
        return extractBatchIdFromTask(task);
    }

    private Long extractBatchIdFromTask(WorkflowTask task) {
        if (task != null && "PROCESS_BATCH".equals(task.getBizType())) {
            return task.getBizId();
        }
        return task != null ? task.getBizId() : null;
    }

    /**
     * 获取操作员信息
     */
    private OperatorInfo getOperatorInfo(Long operatorId, String operatorName, String operatorAccount) {
        OperatorInfo info = new OperatorInfo();
        if (operatorId != null) {
            try {
                User user = userMapper.selectById(operatorId);
                if (user != null) {
                    info.setOperatorId(user.getId());
                    info.setOperatorName(user.getUserName());
                    info.setOperatorAccount(user.getUserAccount());
                    log.debug("成功获取操作员信息: operatorId={}, operatorName={}", operatorId, user.getUserName());
                    return info;
                } else {
                    log.warn("未找到指定的操作员: operatorId={}", operatorId);
                }
            } catch (Exception e) {
                log.warn("查询操作员信息失败: operatorId={}", operatorId, e);
            }
        }
        info.setOperatorId(operatorId);
        info.setOperatorName(operatorName);
        info.setOperatorAccount(operatorAccount);
        log.debug("使用传入的操作员信息: operatorId={}, operatorName={}", operatorId, operatorName);
        return info;
    }

    /**
     * LocalDateTime 转 Date
     */
    private Date toDate(LocalDateTime localDateTime) {
        if (localDateTime == null) return null;
        return Date.from(localDateTime.atZone(java.time.ZoneId.systemDefault()).toInstant());
    }

    private void checkAndCompleteOrder(WorkflowTask task, Long loginUserId, String loginUserName) {
        if (task == null) {
            log.debug("任务对象为空，跳过订单完成检查");
            return;
        }

        // ========== ✅ 修复：通过 batchId 获取 processSheetId ==========
        Long batchId = task.getBizId();  // 任务的 bizId 就是批次ID
        if (batchId == null) {
            log.debug("任务缺少批次ID，跳过订单完成检查: taskId={}", task.getId());
            return;
        }

        // 查询当前批次
        ProcessBatch currentBatch = processBatchMapper.selectById(batchId);
        if (currentBatch == null || currentBatch.getIsDeleted() == 1) {
            log.warn("批次不存在或已删除: batchId={}", batchId);
            return;
        }

        Long processSheetId = currentBatch.getProcessSheetId();
        if (processSheetId == null) {
            log.warn("批次未关联工艺单: batchId={}", batchId);
            return;
        }

        log.info("开始检查订单完成状态: batchId={}, processSheetId={}, orderNumber={}",
                batchId, processSheetId, currentBatch.getOrderNumber());

        try {
            // ========== ✅ 修复：通过 processSheetId 查询所有批次 ==========
            LambdaQueryWrapper<ProcessBatch> qw = new LambdaQueryWrapper<>();
            qw.eq(ProcessBatch::getProcessSheetId, processSheetId)
                    .eq(ProcessBatch::getIsDeleted, 0);
            List<ProcessBatch> allBatches = processBatchMapper.selectList(qw);

            if (allBatches == null || allBatches.isEmpty()) {
                log.warn("未找到工艺单的批次数据: processSheetId={}", processSheetId);
                return;
            }

            long completedCount = allBatches.stream()
                    .filter(b -> "COMPLETED".equals(b.getStatus()))
                    .count();

            log.info("工艺单批次完成情况: processSheetId={}, 总批次={}, 已完成={}",
                    processSheetId, allBatches.size(), completedCount);

            // ========== 所有批次完成，触发创建检验报告任务 ==========
            if (completedCount == allBatches.size()) {
                log.info("✅ 工艺单所有批次已完成，触发检验报告任务: processSheetId={}", processSheetId);

                // 获取订单号和产品名称
                String orderNumber = currentBatch.getOrderNumber();
                String productName = currentBatch.getProductName();

                // ✅ 调用 createInspectionReportTask（基于工艺单）
                taskCreationService.createInspectionReportTask(
                        processSheetId,
                        orderNumber,
                        productName,
                        loginUserId,
                        loginUserName
                );

                // ========== 更新订单状态为 INSPECTING ==========
                if (StringUtils.isNotBlank(orderNumber)) {
                    SalesOrder order = salesOrderService.getByOrderNumber(orderNumber);
                    if (order != null) {
                        salesOrderService.updateOrderStatus(
                                order.getId(),
                                "INSPECTING",
                                "生产完成，待最终检验",
                                null
                        );
                        log.info("✅ 订单状态已更新为 INSPECTING: orderId={}, orderNumber={}",
                                order.getId(), orderNumber);
                    } else {
                        log.warn("未找到订单信息: orderNumber={}", orderNumber);
                    }
                }
            } else {
                log.info("工艺单仍有未完成批次: processSheetId={}, 待完成批次数={}",
                        processSheetId, allBatches.size() - completedCount);
            }
        } catch (Exception e) {
            log.error("检查订单完成状态失败: processSheetId={}", processSheetId, e);
        }
    }
    // ==================== 内部类 ====================

    /**
     * 操作员信息
     */
    @Data
    private static class OperatorInfo {
        private Long operatorId;
        private String operatorName;
        private String operatorAccount;
    }
}